#include "main.h"
#include "stm32f0xx_hal.h"
#include "cmsis_os.h"
#include "i2c.h"
#include "tim.h"
#include "gpio.h"
#include "lcd1602.h"
#include "string.h"

#define LCD_EN_H()        HAL_GPIO_WritePin(LCDEN_GPIO_Port,LCDEN_Pin,GPIO_PIN_SET) 
#define LCD_EN_L()        HAL_GPIO_WritePin(LCDEN_GPIO_Port,LCDEN_Pin,GPIO_PIN_RESET)    
#define LCD_RS_CMD()      HAL_GPIO_WritePin(LCDRS_GPIO_Port,LCDRS_Pin,GPIO_PIN_RESET)
#define LCD_RS_DATA()     HAL_GPIO_WritePin(LCDRS_GPIO_Port,LCDRS_Pin,GPIO_PIN_SET)
#define LCD_RW_READ()     HAL_GPIO_WritePin(LCDRW_GPIO_Port,LCDRW_Pin,GPIO_PIN_SET)
#define LCD_RW_WRITE()    HAL_GPIO_WritePin(LCDRW_GPIO_Port,LCDRW_Pin,GPIO_PIN_RESET)



uint8_t lcd1602DisMode;
uint8_t lcd1602ShiftTimes;
uint8_t lcd1602ShiftCounter;        //1602滚动移动位数计数
uint8_t lcd1602BackLightRank=3;     //默认刚上电背光等级为3  最小0，最大5
lcd1602DisplayStruct lcd1602Line1Dis,lcd1602Line2Dis;

osMailQId  lcd1602MailId;
osMailQDef(lcd1602Mail, 16,lcd1602DisplayStruct);     // Define mail queue

extern osMutexId lcdWriteMutexHandle;


void user_Lcd1602Init(void);
void user_Lcd1602WriteCmd(uint8_t cmd);
void user_Lcd1602WriteData(uint8_t data);
uint8_t user_LcdDisplay(lcd1602DisplayStruct *ldps);
void user_Lcd1602ShiftLeft(void);
void user_Lcd1602ResetDisPos(void);
void user_Lcd1602CleanScreen(void);

void Lcd1602EventTask(void const * argument)
{
  /* USER CODE BEGIN Lcd1602EventTask */
  /* Infinite loop */
	user_Lcd1602Init();
	
//	lcd1602Line1Dis.size=3;
//	memcpy(lcd1602Line1Dis.buffer,(uint8_t *)"wzt",3);
//	lcd1602Line1Dis.position=2;
//	user_LcdDisplay(&lcd1602Line1Dis);
	
	
	lcd1602DisplayStruct *pdis;
  osEvent  evt;
	lcd1602MailId = osMailCreate(osMailQ(lcd1602Mail), NULL);  //create mail queue
	
  for(;;)
  {
		evt = osMailGet(lcd1602MailId, osWaitForever);
		if(evt.status==osEventMail)
		{
			pdis=evt.value.p;
			if(pdis->lineNum==LCD_LINE1)
			{
				lcd1602Line1Dis.disMod=pdis->disMod;
				lcd1602Line1Dis.position=pdis->position;
				lcd1602Line1Dis.size=pdis->size;
				memcpy(lcd1602Line1Dis.buffer,pdis->buffer,lcd1602Line1Dis.size);
				user_LcdDisplay(&lcd1602Line1Dis);
			}
			else if(pdis->lineNum==LCD_LINE2)
			{
				lcd1602Line2Dis.disMod=pdis->disMod;
				lcd1602Line2Dis.position=pdis->position;
				lcd1602Line2Dis.size=pdis->size;
				memcpy(lcd1602Line2Dis.buffer,pdis->buffer,lcd1602Line2Dis.size);
				user_LcdDisplay(&lcd1602Line2Dis);
			}
			else if(pdis->lineNum==LCD_LINE_NONE)  //清屏操作 
			{
				lcd1602Line1Dis.disMod=LCD_DIS_MODE_NORMAL;
				lcd1602Line2Dis.disMod=LCD_DIS_MODE_NORMAL;
				lcd1602Line1Dis.size=0;
				lcd1602Line2Dis.size=0;
				lcd1602DisMode=LCD_DIS_MODE_NORMAL;
				user_Lcd1602CleanScreen();
				
			}
		}
		osMailFree(lcd1602MailId, pdis);
		
  }
  /* USER CODE END Lcd1602EventTask */ 
}
//lcd滚动刷新显示任务
void Lcd1602RollTask(void const * argument)
{

  /* Infinite loop */
	static uint8_t counter;
  for(;;)
  {
		if(lcd1602DisMode==LCD_DIS_MODE_ROLL) 
		{
			user_Lcd1602ShiftLeft();   //向左滚动
			lcd1602ShiftCounter++;
			if(lcd1602ShiftCounter>=lcd1602ShiftTimes)
			{
				lcd1602ShiftCounter=0;
				user_Lcd1602ResetDisPos();
			}
		}
		else
		{
			lcd1602ShiftCounter=0;
		}
		HAL_GPIO_TogglePin(TESTLED_GPIO_Port,TESTLED_Pin);
    osDelay(500);
  }
}

//把从命令接收到的数据进行打包成mail并发送出去
//buf是从命令直接接收到的数据，size也是接收到的数量，还需要本函数进一步解析
void user_Lcd1602SendMail(uint8_t line,uint8_t disMode,uint8_t* buf,uint8_t size)
{ 
	uint8_t tmpSize;
	lcd1602DisplayStruct *pdis;
	if(size>17)   //最多显示16byte+1byte显示位置
	{
		tmpSize=16;
	}
	else tmpSize=size-1;    //除去第一个字节代表显示位置
	pdis = osMailAlloc(lcd1602MailId, osWaitForever);
	pdis ->size=tmpSize;
	pdis->lineNum=line;
	pdis->disMod=disMode;
	pdis->position=buf[0];
	memcpy(pdis->buffer,buf+1,tmpSize);
	osMailPut(lcd1602MailId,pdis);
	
}
//1602专用延时函数
void user_Lcd1602Delay(uint32_t time)
{
	osDelay(time);
//	HAL_Delay(time);
}
//1602清屏操作
void user_Lcd1602CleanScreen()
{
	osMutexWait(lcdWriteMutexHandle, osWaitForever);    //lcd1602读写操作其他任务也可能调用，所以这里需要互斥
	user_Lcd1602WriteCmd(0x01);
	osMutexRelease(lcdWriteMutexHandle);                //互斥资源使用完以后要释放掉
}
//1602屏幕左移一位
void user_Lcd1602ShiftLeft()
{
	osMutexWait(lcdWriteMutexHandle, osWaitForever);    //lcd1602读写操作其他任务也可能调用，所以这里需要互斥
	user_Lcd1602WriteCmd(0x18);
	osMutexRelease(lcdWriteMutexHandle);                //互斥资源使用完以后要释放掉
}
//1602复位显示位置光标
void user_Lcd1602ResetDisPos()
{
	osMutexWait(lcdWriteMutexHandle, osWaitForever);    //lcd1602读写操作其他任务也可能调用，所以这里需要互斥
	user_Lcd1602WriteCmd(0x02);
	osMutexRelease(lcdWriteMutexHandle);                //互斥资源使用完以后要释放掉
	lcd1602ShiftCounter=0;
}
//亮度等级在0-5级
void user_Lcd1602BackLight(uint8_t rank)
{
	if(rank>5)rank=5;
	lcd1602BackLightRank=rank;
	uint16_t timerValue;
	timerValue=rank*100;
	TIM14->CCR1 =timerValue;
}
//根据数据显示
uint8_t user_LcdDisplay(lcd1602DisplayStruct *ldps)
{	
	uint8_t i;
	uint8_t tmpAdd;
	if((ldps->position>16)||(ldps->position==0))   //如果写的位置大于16或者等于0直接忽略掉
	{
		return 0;
	}
	if((ldps->position+ldps->size)>17)  //显示的最大偏移量超过了16
	{
		ldps->size=17-ldps->position;     //
	}
	if(ldps->disMod==LCD_DIS_MODE_NORMAL) //正常显示模式
	{
		//设置起始显示地址
		if(ldps->lineNum==LCD_LINE1)
		{
			tmpAdd=0x80+ldps->position-1;
			
		}
		else 
		{
			tmpAdd=0x80+0x40+ldps->position-1;
			
		}
	}
	else                                 //滚动显示模式
	{
		//设置起始显示地址
		if(ldps->lineNum==LCD_LINE1)
		{
			tmpAdd=0x80+0X10+ldps->position-1;
			if(lcd1602DisMode==LCD_DIS_MODE_NORMAL)   //如果是从正常模式切换过来的，删除另一行的长度
			{
				lcd1602Line2Dis.size=0;
			}
		}
		else 
		{
			tmpAdd=0x80+0x50+ldps->position-1;
			if(lcd1602DisMode==LCD_DIS_MODE_NORMAL)   //如果是从正常模式切换过来的，删除另一行的长度
			{
				lcd1602Line1Dis.size=0;
			}
		}
		//更新滚动移位次数
		if((lcd1602Line1Dis.size+lcd1602Line1Dis.position)>(lcd1602Line2Dis.size+lcd1602Line2Dis.position))
		{
			lcd1602ShiftTimes=16+lcd1602Line1Dis.size+lcd1602Line1Dis.position-1;
		}
		else
		{
			lcd1602ShiftTimes=16+lcd1602Line2Dis.size+lcd1602Line2Dis.position-1;
		}
	}
	if(lcd1602DisMode!=ldps->disMod)           //如果显示模式改变了，需要进行清屏操作
	{
			user_Lcd1602CleanScreen();
			lcd1602DisMode=ldps->disMod;
	}
	//更新本次设置的这一行显示数据
  osMutexWait(lcdWriteMutexHandle, osWaitForever);    //lcd1602读写操作其他任务也可能调用，所以这里需要互斥
	user_Lcd1602WriteCmd(tmpAdd);                       //position从1开始，显示地址从0开始
	for(i=0;i<ldps->size;i++)
	{
		user_Lcd1602WriteData(*(ldps->buffer+i));
	}
	osMutexRelease(lcdWriteMutexHandle);                //互斥资源使用完以后要释放掉
	if(ldps->disMod==LCD_DIS_MODE_ROLL)
	{
		user_Lcd1602ResetDisPos();
	}
	
}

void user_Lcd1602WriteCmd(uint8_t cmd)
{
	LCD_RS_CMD();
	GPIOA->ODR&=0XFF00;
	GPIOA->ODR|=cmd;
	LCD_EN_H();
	user_Lcd1602Delay(2);
	LCD_EN_L();	
}
void user_Lcd1602WriteData(uint8_t data)
{
	LCD_RS_DATA();
	GPIOA->ODR&=0XFF00;
	GPIOA->ODR|=data;
	LCD_EN_H();
	user_Lcd1602Delay(2);
	LCD_EN_L();	
}
void user_Lcd1602Init()
{
	LCD_RW_WRITE();
	user_Lcd1602Delay(5);
	user_Lcd1602WriteCmd(0X38);
	user_Lcd1602WriteCmd(0X0C);
	user_Lcd1602WriteCmd(0X06);
	user_Lcd1602WriteCmd(0X01);
	HAL_TIM_PWM_Start(&htim14,TIM_CHANNEL_1);    //打开屏幕背光pwm输出
	user_Lcd1602BackLight(3);
	
	lcd1602Line1Dis.lineNum=LCD_LINE1;
	lcd1602Line2Dis.lineNum=LCD_LINE2;
	lcd1602Line1Dis.disMod=LCD_DIS_MODE_NORMAL;  //初始都显示正产模式
	lcd1602Line2Dis.disMod=LCD_DIS_MODE_NORMAL;
	lcd1602DisMode=LCD_DIS_MODE_NORMAL;
	
	lcd1602BackLightRank=3;
}
/**************************按键相关代码***************************/    
uint8_t keyValue[2];
uint8_t keyState[2];

//按键状态机
#define KEY_STATE_NULL               0X00
#define KEY_STATE_PRESS_FIRST        0X01
#define KEY_STATE_PRESS_OK           0X02   //经过滤波后确定按下
#define KEY_STATE_PRESS_LONG         0X03   //按键长按两秒
#define KEY_STATE_RELEASE            0X05   //按键释放
#define KEY_STATE_SHORT_RELEASE      0X06
#define KEY_STATE_LONG_RELEASE       0X07 

void user_Lcd1602KeyScan()
{
	uint8_t i=0;
	static uint16_t keyPressTime[2];
	
	keyValue[0]=HAL_GPIO_ReadPin(KEYJIA_GPIO_Port,KEYJIA_Pin);
	keyValue[1]=HAL_GPIO_ReadPin(KEYJIAN_GPIO_Port,KEYJIAN_Pin);
	for(i=0;i<2;i++)
	{
		switch (keyState[i])
		{
			case KEY_STATE_NULL:
				if(keyValue[i]==0)
				{
					keyState[i]=KEY_STATE_PRESS_FIRST;
					keyPressTime[i]=0;   //首次按键按下，把计数值清0
				}
				break;
		  case KEY_STATE_PRESS_FIRST:
				keyPressTime[i]++; 
				if(keyPressTime[i]>=3)    //等待延时40ms用来滤波
				{
					if(keyValue[i]==0)
					{
						keyState[i]=KEY_STATE_PRESS_OK;
						keyPressTime[i]=0;   //确定按键按下，就把计数值清0
					}
					else
					{
						keyState[i]=KEY_STATE_NULL;
					}				
				}
				break;
			case KEY_STATE_PRESS_OK:
				keyPressTime[i]++;
				if(keyValue[i]==1)
				{
					keyState[i]=KEY_STATE_SHORT_RELEASE;
				}	
				else 
				{
					if(keyPressTime[i]>=200)   //按键超过2秒钟判断为长按键
					{
						  keyState[i]=KEY_STATE_PRESS_LONG;
						  keyPressTime[i]=0;    //计数器清0
						  /*******************LONG PRESS DEAL BEGIN*************************/
						  
							/*******************LONG PRESS DEAL END  *************************/
					}
				}
				break;
			case KEY_STATE_PRESS_LONG:
				if(keyValue[i]==1)
				{
					keyState[i]=KEY_STATE_LONG_RELEASE;							
						
				}
				break;
			case KEY_STATE_LONG_RELEASE:
				if(keyValue[i]==1)  //be sure the key is pressed again
				{
					keyState[i]=KEY_STATE_NULL;
					/*******************LONG RELEASE DEAL BEGIN*************************/
				  NVIC_SystemReset();
					/*******************LONG RELEASE DEAL END  *************************/
				}
				else
				{
					keyState[i]=KEY_STATE_PRESS_LONG;
				}
				break;				
			case KEY_STATE_SHORT_RELEASE:
				if(keyValue[i]==1)   //be sure the key is pressed again
				{
					
					keyState[i]=KEY_STATE_NULL;	
					/*******************SHORT RELEASE DEAL BEGIN*************************/
//				  HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
					if(i==0)
					{
						if(lcd1602BackLightRank<5)lcd1602BackLightRank++;
						user_Lcd1602BackLight(lcd1602BackLightRank);
					}
					else
					{
						if(lcd1602BackLightRank!=0)lcd1602BackLightRank--;
						user_Lcd1602BackLight(lcd1602BackLightRank);
					}
					/*******************SHORT RELEASE DEAL END  *************************/
				}
				else
				{
					keyState[i]=KEY_STATE_PRESS_OK;
				}
				break;
		}
	}		
}
